package org.budo.dubbo.protocol.http.view.render.budo;

import org.budo.dubbo.protocol.http.aop.permission.app.AppPermissionCheckException;
import org.budo.dubbo.protocol.http.aop.permission.user.UserPermissionCheckException;
import org.budo.dubbo.protocol.http.exception.remoting.ArgumentTimeFormatException;
import org.budo.dubbo.protocol.http.exception.remoting.ArgumentTypeMismatchException;
import org.budo.dubbo.protocol.http.exception.remoting.ArrayArgumentMismatchException;
import org.budo.dubbo.protocol.http.exception.remoting.AuthenticationCheckException;
import org.budo.dubbo.protocol.http.exception.remoting.AuthorizeFailException;
import org.budo.dubbo.protocol.http.exception.remoting.EnumArgumentMismatchException;
import org.budo.dubbo.protocol.http.exception.remoting.InterfaceMissMatchException;
import org.budo.dubbo.protocol.http.exception.remoting.MethodMissMatchException;
import org.budo.dubbo.protocol.http.exception.remoting.SignCheckException;
import org.budo.dubbo.protocol.http.view.util.ViewRenderUtil;
import org.budo.support.assertion.AssertException;
import org.budo.support.exception.business.BusinessException;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.mvcs.Mvcs;
import org.budo.validation.exception.AbstractBudoValidationException;

import com.alibaba.dubbo.rpc.RpcException;

import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Slf4j
public class ExceptionRender {
    private AuthenticationCheckExceptionRender authenticationCheckExceptionRender = new AuthenticationCheckExceptionRender();

    private SignCheckExceptionRender signCheckExceptionRender = new SignCheckExceptionRender();

    private ValidationExceptionRender validationExceptionRender = new ValidationExceptionRender();

    private AssertExceptionRender assertExceptionRender = new AssertExceptionRender();

    public ApiError buildApiError(Throwable exception) {
        if (exception instanceof InterfaceMissMatchException) {
            log.error("#40 buildApiError, InterfaceMissMatchException, ", exception);
            return ApiError.INTERFACE_MISS_MATCH;
        }

        if (exception instanceof MethodMissMatchException) {
            return ApiError.METHOD_MISS_MATCH;
        }

        if (exception instanceof AuthenticationCheckException) {
            return authenticationCheckExceptionRender.buildApiError((AuthenticationCheckException) exception);
        }

        if (exception instanceof SignCheckException) {
            return signCheckExceptionRender.buildApiError((SignCheckException) exception);
        }

        if (exception instanceof AbstractBudoValidationException) { // 注解参数检查
            return validationExceptionRender.buildApiError((AbstractBudoValidationException) exception);
        }

        if (exception instanceof AssertException) {
            return assertExceptionRender.buildApiError((AssertException) exception);
        }

        if (exception instanceof UserPermissionCheckException) {
            return ApiError.USER_PERMISSION_ERROR.withDescription(exception.getMessage());
        }

        if (exception instanceof AppPermissionCheckException) {
            return ApiError.APP_PERMISSION_ERROR.withDescription(exception.getMessage());
        }

        if (exception instanceof BusinessException) {
            return ApiError.BUSINESS_ERROR.withDescription(exception.getMessage());
        }

        if (exception instanceof ArrayArgumentMismatchException) { // 数组参数错误
            ArrayArgumentMismatchException ex = (ArrayArgumentMismatchException) exception;
            String description = "数组参数 " + ex.getParameterName() + "(值 " + ex.getArgumentValue() + ")错误, 需要传入一个数组";
            ApiError error = ApiError.ILLEGAL_ARRAY_PARAMETER.withDescription(description);
            return error;
        }

        if (exception instanceof EnumArgumentMismatchException) { // 枚举参数错误
            EnumArgumentMismatchException ex = (EnumArgumentMismatchException) exception;
            Class<?> parameterType = ex.getParameterType();
            String description = "枚举参数 " + ex.getParameterName() + "(值 " + ex.getArgumentValue() + ")错误, 需要" + "[" + StringUtil.join(parameterType.getEnumConstants()) + "]";
            ApiError error = ApiError.ILLEGAL_ENUM_PARAMETER.withDescription(description);
            return error;
        }

        if (exception instanceof ArgumentTypeMismatchException) { // 参数类型错误
            ArgumentTypeMismatchException ex = (ArgumentTypeMismatchException) exception;
            String argumentValueType = null == ex.getArgumentValue() ? null : ex.getArgumentValue().getClass().getName();
            String description = "参数 " + ex.getParameterName() + "(类型 " + argumentValueType + ")错误, 需要" + ex.getParameterType().getCanonicalName();
            ApiError error = ApiError.PARAMETER_TYPE_MISMATCH.withDescription(description);
            return error;
        }

        if (exception instanceof ArgumentTimeFormatException) {
            ArgumentTimeFormatException ex = (ArgumentTimeFormatException) exception;
            String description = "时间参数 " + ex.getParameterName() + "(#" + ex.getParameterIndex() + ") 格式错误, 只支持 " + StringUtil.join(ex.getTimeFormats());
            ApiError error = ApiError.NOT_SUPPORTED_TIME_FORMAT.withDescription(description);
            return error;
        }

        if (exception instanceof IllegalArgumentException) {
            log.error("#46 IllegalArgumentException=" + exception + ", requestURI=" + Mvcs.getRequestURI());
            return ApiError.ILLEGAL_PARAMETER.withDescription(exception.getMessage());
        }

        if (exception instanceof AuthorizeFailException) {
            AuthorizeFailException authorizeFailException = (AuthorizeFailException) exception;
            return authorizeFailException.getApiError();
        }

        // RpcException
        String error = "" + exception;
        if (exception instanceof RpcException) {
            String noservice = "No provider available for the service";
            int a = error.indexOf(noservice);
            if (a > 0) {
                int b = error.indexOf("from registry", a);
                String interfaceType = error.substring(a + noservice.length(), b).trim();
                log.error("#120 interfaceType=" + interfaceType + ", RpcException=" + exception + ", requestURI=" + Mvcs.getRequestURI(), exception);
                return ApiError.NO_PROVIDER_ERROR.withDescription("no provider for service " + interfaceType);
            }

            log.error("#124 RpcException=" + exception + ", requestURI=" + Mvcs.getRequestURI(), exception);

            if (error.contains("RpcException: Forbid consumer")) {
                String forbidAccessService = ViewRenderUtil.forbidAccessService(exception);
                return ApiError.FORBID_ACCESS_ERROR.withDescription(forbidAccessService);
            }

            return ApiError.RPC_ERROR;
        }

        if (error.contains("java.sql.SQLException: Query execution was interrupted, max_statement_time exceeded")) {
            log.warn("#130 render SQLException, exception=" + exception + ", requestURI=" + Mvcs.getRequestURI(), exception);
            return ApiError.MAX_STATEMENT_TIME;
        }

        if (error.contains("java.lang.NullPointerException:")) {
            log.warn("#136 render NullPointerException, exception=" + exception + ", requestURI=" + Mvcs.getRequestURI(), exception);
            return ApiError.NULL_POINTER;
        }

        if (error.contains("Unknown column")) {
            log.warn("#142 render Unknown column, exception=" + exception + ", requestURI=" + Mvcs.getRequestURI(), exception);
            return ApiError.UNKNOWN_COLUMN;
        }

        // 其他异常
        log.error("#147 renderException else, exception=" + exception + ", requestURI=" + Mvcs.getRequestURI(), exception);
        return ApiError.SERVER_ERROR;
    }
}